package com.cuixichang.minimalism.domain;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cuixichang.minimalism.acl.RelevanceDesign;
import com.cuixichang.minimalism.basal.enums.DefaultNumberEnum;
import com.cuixichang.minimalism.core.mybatis.cnstants.BaseFieldNames;
import com.cuixichang.minimalism.core.security.userdetails.CredentialDetail;
import com.cuixichang.minimalism.service.*;
import com.cuixichang.minimalism.transfer.dto.*;
import com.cuixichang.minimalism.transfer.po.*;
import com.sun.istack.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class IRoleCommandServiceImpl implements IRoleCommandService{
    @Autowired
    private IRoleService iRoleService;
    @Autowired
    private ILoginRoleService iLoginRoleService;
    @Autowired
    private IMenuService iMenuService;
    @Autowired
    private IRoleMenuService iRoleMenuService;
    @Autowired
    private IRolePermissionService iRolePermissionService;
    @Autowired
    private RelevanceDesign relevanceDesign;
    @Autowired
    private IPermissionService iPermissionService;
    @Autowired
    private IRouterService routerService;
    @Autowired
    private IRouterRelationService routerRelationService;

    @Override
    @Transactional
    public IRoleDTO insert(IRoleDTO roleDTO, CredentialDetail credentialDetail) {
        Assert.notNull(roleDTO,"角色信息不能为空,请检查");
        String roleCode = roleDTO.getRoleCode();
        Assert.hasText(roleCode,"角色编码不能为空,请检查");
        String createSystemCode = roleDTO.getCreateSystemCode();
        Assert.hasText(createSystemCode,"系统编码不能为空,请检查");
        roleDTO.setCreateLoginId(Objects.nonNull(credentialDetail)?credentialDetail.getLoginId(): DefaultNumberEnum.Zero.getCode());
        IRoleDTO checkRole = iRoleService.selectOne(IRoleDTO.builder().roleCode(roleCode)
                .createSystemCode(createSystemCode).build());
        Assert.isNull(checkRole,"角色编码已存在,请检查");
        return iRoleService.insert(roleDTO);
    }

    @Override
    public Page<IRoleDTO> loginHasBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize, Long loginId, CredentialDetail credentialDetail) {
        checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(loginId,"查询账号Id为空,请检查");
        if(Objects.nonNull(credentialDetail)){
            Long  credentialLoginId = credentialDetail.getLoginId();
            if(!credentialLoginId.equals(loginId)){
                List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credentialDetail.getLoginId());
                Assert.isTrue(beAuthLoginIds.contains(loginId),"当前用户无权限操作");
            }
        }
        List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(loginId).build());
        if(CollectionUtils.isEmpty(iLoginRoles)) return new Page<>(pageNo,pageSize);
        List<Long> hasBindingRoleIds = iLoginRoles.stream().map(IL -> IL.getRoleId()).distinct().collect(Collectors.toList());
        return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
            processBindingRoleQueryWrapper(queryWrapper,hasBindingRoleIds,roleDTO.getKeyword());
        });
    }

    @Override
    public Page<IRoleDTO> menuHasBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize,Long menuId, CredentialDetail credentialDetail) {
        checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(menuId,"查询菜单Id为空,请检查");
        IMenuDTO menuDetail = Optional.ofNullable(credentialDetail).map(credential ->
            iMenuService.selectList(IMenuDTO.builder().build(), (menu, queryWrapper) -> {
            queryWrapper.eq(BaseFieldNames.ID, menuId);
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds), BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
        }).stream().findFirst().orElse(null)).orElseGet(() -> iMenuService.selectById(menuId));
        Page<IRoleDTO> page = new Page<>(pageNo,pageSize);
        if(Objects.isNull(menuDetail)) return page;
        List<IRoleMenuDTO> iRoleMenus = iRoleMenuService.selectList(IRoleMenuDTO.builder().menuId(menuDetail.getId()).isEnable(true).build());
        List<Long> roleIds = iRoleMenus.stream().map(IRoleMenuDTO::getRoleId).distinct().collect(Collectors.toList());
        if(CollectionUtils.isEmpty(roleIds)) return page;
        return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
            processBindingRoleQueryWrapper(queryWrapper,roleIds,roleDTO.getKeyword());
        });
    }

    @Override
    public Page<IRoleDTO> permissionHasBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize, Long permissionId, CredentialDetail credentialDetail) {
       checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(permissionId,"查询权限Id为空,请检查");
        IPermissionDTO po = Optional.ofNullable(credentialDetail).map(credential ->
            iPermissionService.selectList(IPermissionDTO.builder().build(), (var, queryWrapper) -> {
            queryWrapper.eq(BaseFieldNames.ID, permissionId);
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds), BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
        }).stream().findFirst().orElse(null)).orElseGet(() -> iPermissionService.selectById(permissionId));
        Page<IRoleDTO> page = new Page<>(pageNo,pageSize);
        if(Objects.isNull(po)) return page;
        List<IRolePermissionDTO> iRolePermissions = iRolePermissionService.selectList(IRolePermissionDTO.builder().permissionId(po.getId())
                .isEnable(true).build());
        List<Long> roleIds = iRolePermissions.stream().map(IRolePermissionDTO::getRoleId).distinct().collect(Collectors.toList());
        if(CollectionUtils.isEmpty(roleIds)) return page;
        return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
            processBindingRoleQueryWrapper(queryWrapper,roleIds,roleDTO.getKeyword());
        });
    }

    @Override
    public List<IRoleDTO> loginHasBindingRoles(IRoleDTO roleDTO, String systemCode, Long loginId, Long parentLoginId) {
        Assert.notNull(roleDTO,"角色信息不能为空,请检查");
        roleDTO.setCreateSystemCode(systemCode);
        Assert.notNull(loginId,"查询账号Id为空,请检查");
        if(Objects.nonNull(parentLoginId)){
            if(!parentLoginId.equals(loginId)){
                List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(parentLoginId);
                Assert.isTrue(beAuthLoginIds.contains(loginId),"当前用户无权限操作");
            }
        }
        List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(loginId).build());
        if(CollectionUtils.isEmpty(iLoginRoles)) return Collections.emptyList();
        List<Long> hasBindingRoleIds = iLoginRoles.stream().map(IL -> IL.getRoleId()).distinct().collect(Collectors.toList());
        return iRoleService.selectList(roleDTO,(iRole, queryWrapper) -> {
             processBindingRoleQueryWrapper(queryWrapper,hasBindingRoleIds,roleDTO.getKeyword());
        });
    }

    @Override
    public Page<IRoleDTO> loginUnBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize, Long loginId, CredentialDetail credentialDetail) {
        checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(loginId,"查询账号Id为空,请检查");
        return Optional.ofNullable(credentialDetail).map(credential -> {
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(credential.getLoginId()).build());
            if (!beAuthLoginIds.contains(loginId)) return new Page<IRoleDTO>(pageNo, pageSize);
            List<Long> hasBindingRoleIds = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(loginId).build()).stream().map(ILoginRoleDTO::getRoleId)
                    .distinct().collect(Collectors.toList());
            return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
                processUnBindingRoleQueryWrapper(queryWrapper,hasBindingRoleIds,iLoginRoles.stream().map(ILoginRoleDTO::getRoleId).distinct()
                                .collect(Collectors.toList()), roleDTO.getKeyword(),beAuthLoginIds);
            });
        }).orElseGet(() -> new Page<IRoleDTO>(pageNo, pageSize));
    }

    @Override
    public Page<IRoleDTO> menuUnBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize, Long menuId, CredentialDetail credentialDetail) {
        checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(menuId,"查询菜单Id为空,请检查");
        return Optional.ofNullable(credentialDetail).map(credential -> {
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(credential.getLoginId()).build());
            List<Long> hasBindingRoleIds = iRoleMenuService.selectList(IRoleMenuDTO.builder().menuId(menuId).isEnable(true).build()).stream().map(IRoleMenuDTO::getRoleId)
                    .distinct().collect(Collectors.toList());
            return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
                 processUnBindingRoleQueryWrapper(queryWrapper,hasBindingRoleIds,iLoginRoles.stream().map(ILoginRoleDTO::getRoleId).distinct()
                                .collect(Collectors.toList()), roleDTO.getKeyword(),beAuthLoginIds);
            });
        }).orElseGet(() -> new Page<IRoleDTO>(pageNo, pageSize));
    }

    @Override
    public Page<IRoleDTO> permissionUnBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize, Long permissionId, CredentialDetail credentialDetail) {
        checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(permissionId,"查询权限Id为空,请检查");
        return Optional.ofNullable(credentialDetail).map(credential -> {
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(credential.getLoginId()).build());
            List<Long> hasBindingRoleIds = iRolePermissionService.selectList(IRolePermissionDTO.builder().permissionId(permissionId).isEnable(true).build())
                    .stream().map(IRolePermissionDTO::getRoleId).distinct().collect(Collectors.toList());
            return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
                 processUnBindingRoleQueryWrapper(queryWrapper,hasBindingRoleIds,iLoginRoles.stream().map(ILoginRoleDTO::getRoleId).distinct()
                                .collect(Collectors.toList()), roleDTO.getKeyword(),beAuthLoginIds);
            });
        }).orElseGet(() -> new Page<IRoleDTO>(pageNo, pageSize));
    }

    @Override
    public boolean menuBindingStatus(Long roleId, Long menuId, boolean toBinding, CredentialDetail credentialDetail) {
        Assert.notNull(roleId,"角色Id不能为空,请检查");
        Assert.notNull(menuId,"菜单Id不能为空,请检查");
        boolean invoke = false;
        List beAuthLoginIds = Optional.ofNullable(credentialDetail).map(credential -> relevanceDesign.beAuthLoginIds(credential.getLoginId()))
                .orElse(Collections.EMPTY_LIST);
        if(Objects.isNull(selectByIdHasPreCheck(roleId, credentialDetail.getSystemCode(), credentialDetail))) return invoke;
        IMenuDTO menu = Optional.ofNullable(credentialDetail).map(credential ->
           iMenuService.selectList(IMenuDTO.builder().build(), (iMenu, queryWrapper) -> {
             queryWrapper.eq(BaseFieldNames.ID, menuId);
             queryWrapper.and(wrapper -> {
                 wrapper.in(BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
             });
         }).stream().findFirst().orElse(null)).orElseGet(() -> iMenuService.selectById(menuId));
        if(Objects.isNull(menu)) return invoke;
        IRoleMenuDTO build = IRoleMenuDTO.builder().roleId(roleId).menuId(menuId).build();
        if(toBinding) {
            build.setCreateLoginId(Optional.ofNullable(credentialDetail).map(CredentialDetail::getLoginId).orElse(0L));
            IRoleMenuDTO roleMenu = iRoleMenuService.insertHasOption(build, (dto)-> iRoleMenuService.selectList(build).stream().findFirst().orElse(null));
            invoke = Objects.nonNull(roleMenu);
        }else {
            invoke = iRoleMenuService.deleteByCondition(build) > 0;
        }
        return invoke;
    }

    @Override
    public List<IRoleDTO> selectDefaultRole(String systemCode) {
        Assert.hasText(systemCode,"系统编码不能为空,请检查");
        return iRoleService.selectList(IRoleDTO.builder().isDefault(true)
                .createSystemCode(systemCode).build());
    }

    @Override
    public IRoleDTO selectByIdHasPreCheck(Long id, String systemCode, CredentialDetail credentialDetail) {
        Assert.notNull(id,"角色Id不能为空,请检查");
        Assert.notNull(systemCode,"系统编码不能为空,请检查");
        return Optional.ofNullable(credentialDetail).map(credential -> {
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credentialDetail.getLoginId());
            IRoleDTO innerRole = iRoleService.selectById(id);
            if (Objects.nonNull(innerRole)) {
                if (!systemCode.equals(innerRole.getCreateSystemCode()) || !beAuthLoginIds.contains(innerRole.getCreateLoginId())) {
                    return null;
                }
            }
            return innerRole;
        }).orElseGet(() -> iRoleService.selectById(id));
    }

    @Override
    public IRoleDTO selectByCodeHasPreCheck(String roleCode, String systemCode, CredentialDetail credentialDetail) {
        Assert.hasText(roleCode,"角色编码不能为空,请检查");
        Assert.hasText(systemCode,"系统编码不能为空,请检查");
        return iRoleService.selectOne(IRoleDTO.builder().roleCode(roleCode).createSystemCode(systemCode).build());
    }

    @Override
    @Transactional
    public boolean deleteByIdHasPreCheck(Long id, String systemCode, CredentialDetail credentialDetail) {
        Assert.notNull(id,"角色Id不能为空,请检查");
        Assert.hasText(systemCode,"系统编码不能为空,请检查");
        // 检查是否有权限操作
        Boolean check = Optional.ofNullable(credentialDetail).map(credential -> {
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            IRoleDTO role = iRoleService.selectById(id);
            if (Objects.nonNull(role)) {
                if (beAuthLoginIds.contains(role.getCreateLoginId()) && systemCode.equals(role.getCreateSystemCode())) {
                    return true;
                }
            }
            return false;
        }).orElse(Objects.nonNull(iRoleService.selectById(id)));
        if(!check) return false;
        iRolePermissionService.deleteByCondition(IRolePermissionDTO.builder().roleId(id).build());
        iLoginRoleService.deleteByCondition(ILoginRoleDTO.builder().roleId(id).build());
        return iRoleService.deleteById(id) == 1;
    }

    @Override
    public boolean updateByIdHasPreCheck(IRoleDTO roleDTO, CredentialDetail credentialDetail) {
        Assert.notNull(roleDTO,"角色信息不能为空,请检查");
        Long id = roleDTO.getId();
        Assert.notNull(id,"角色Id不能为空,请检查");
        return iRoleService.updateById(roleDTO,(role, queryWrapper) -> {
            if(Objects.nonNull(credentialDetail)){
                role.setCreateLoginId(credentialDetail.getLoginId());
                List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credentialDetail.getLoginId());
                queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds),BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
            }
        }) == 1;
    }

    @Override
    public Page<IRoleDTO> selectPageByConditional(IRoleDTO roleDTO, Long pageNo, Long pageSize, CredentialDetail credentialDetail) {
        if(Objects.isNull(credentialDetail)) return Page.of(pageNo,pageSize);
        Long createLoginId = credentialDetail.getLoginId();
        return iRoleService.selectPage(roleDTO,pageSize,pageNo,(role, queryWrapper)->{
            if(Objects.nonNull(createLoginId)){
                List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(createLoginId);
                queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds),BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
            }
            String keyword = roleDTO.getKeyword();
            queryWrapper.like(StrUtil.isNotBlank(keyword),"role_name",keyword);
        });
    }

    @Override
    public Page<IRoleDTO> routerHasBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize, Long routerId, CredentialDetail credentialDetail) {
        checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(routerId,"查询路由Id为空,请检查");
        IRouterDTO po = Optional.ofNullable(credentialDetail).map(credential ->
            routerService.selectList(IRouterDTO.builder().build(), (var, queryWrapper) -> {
            queryWrapper.eq("id", routerId);
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds), BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
        }).stream().findFirst().orElse(null)).orElseGet(() -> routerService.selectById(routerId));
        Page<IRoleDTO> page = new Page<>(pageNo,pageSize);
        if(Objects.isNull(po)) return page;
        List<IRouterRelationDTO> relations = routerRelationService.selectList(IRouterRelationDTO.builder().routerId(po.getId())
                .roleRelation(true).isEnable(true).build());
        List<Long> roleIds = relations.stream().map(IRouterRelationDTO::getRelationId).distinct().collect(Collectors.toList());
        if(CollectionUtils.isEmpty(roleIds)) return page;
        return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
            processBindingRoleQueryWrapper(queryWrapper,roleIds,roleDTO.getKeyword());
        });
    }

    @Override
    public Page<IRoleDTO> routerUnBindingRoles(IRoleDTO roleDTO, Long pageNo, Long pageSize, Long routerId, CredentialDetail credentialDetail) {
        checkPageQuery(roleDTO, pageNo, pageSize);
        Assert.notNull(routerId,"查询路由Id为空,请检查");
        return Optional.ofNullable(credentialDetail).map(credential -> {
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId());
            List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().loginId(credential.getLoginId()).build());
            List<Long> hasBindingRoleIds = routerRelationService.selectList(IRouterRelationDTO.builder().routerId(routerId).isEnable(true).roleRelation(true)
                            .build()).stream().map(IRouterRelationDTO::getRelationId).distinct().collect(Collectors.toList());
            return iRoleService.selectPage(roleDTO,pageSize,pageNo,(iRole, queryWrapper) -> {
                 processUnBindingRoleQueryWrapper(queryWrapper,hasBindingRoleIds,iLoginRoles.stream().map(ILoginRoleDTO::getRoleId).distinct()
                                .collect(Collectors.toList()), roleDTO.getKeyword(),beAuthLoginIds);
            });
        }).orElseGet(() -> new Page<IRoleDTO>(pageNo, pageSize));
    }

    private void checkPageQuery(IRoleDTO roleDTO, Long pageNo, Long pageSize){
        Assert.notNull(roleDTO,"角色信息不能为空,请检查");
        Assert.notNull(roleDTO.getCreateSystemCode(),"系统编码不能为空");
        Assert.notNull(pageNo,"页码不能为空");
        Assert.notNull(pageSize,"页大小不能为空");
    }

    private void processBindingRoleQueryWrapper(QueryWrapper queryWrapper,List<Long> includeIds,String keyword){
         queryWrapper.in(!CollectionUtils.isEmpty(includeIds),"id", includeIds);
         queryWrapper.like(StrUtil.isNotBlank(keyword),"role_name",keyword);
    }

    private void processUnBindingRoleQueryWrapper(QueryWrapper<IRole> queryWrapper,List<Long> includeIds,List<Long> excludeIds,String keyword,@NotNull List<Long> includeLoginIds){
        //绑定的角色 + 创建的角色
        queryWrapper.and(wrapper -> {
            wrapper.in(BaseFieldNames.CREATE_LOGIN_ID, includeLoginIds);
            wrapper.or(!CollectionUtils.isEmpty(excludeIds)).in("id", excludeIds);
        });
        // 排除菜单已绑定的角色
        queryWrapper.and(!CollectionUtils.isEmpty(includeIds),wrapper -> wrapper.notIn("id", includeIds));
        queryWrapper.like(StrUtil.isNotBlank(keyword),"role_name",keyword);
    }
}
